/*!
 * \file mapir.h
 *
 * \brief Declarations for the MapIR classes
 *
 * \date Started: 8/5/2010
 * \date Last Modified: 8/5/2010
 * \authors Ian Craig
 * \authors Nicholas Jeanette
 *
 * Copyright (c) 2010, Colorado State University <br>
 * All rights reserved. <br>
 * See ../../COPYING for details. <br>
 */

#ifndef MAPIR_H_
#define MAPIR_H_

#include <iegenlib/iegenlib.h>

#include <string>
using std::string;
#include <map>
using std::map;

using iegenlib::Set;
using iegenlib::Relation;
using iegenlib::Symbolic;

namespace iegen{

//Forward declaration so that MapIR has all the classes it needs
class MapIR;
class MapIREntity;
class DataArray;
class ERSpec;
class IndexArray;
class Statement;
class AccessRelation;
class DataDependence;

/*!
 * \class MapIR
 * \brief Class representing a Mapping Intermediate Representation
 */
class MapIR{
public:

  /*!
   * Constructor for the MapIR
   */
  inline MapIR(){};

  /*!
   * add a symbolic to the MapIR. Takes the parameters to construct the symbolic
   * first. Before adding to the list of symbolics it checks that the symbolic
   * has a unique name in the MapIR
   * @param name string unique name to the map
   * @param lower an integer of the lower bound
   * @param upper an integer of the upper bound
   */
  void add_symbolic(string name, int lower, int upper);

  /*!
   * add a data array to the MapIR. Takes the parameters to construct the data
   * array first. Before adding to the list data_arrays it checks that the name
   * is unique in the MapIR
   * @param name a string that is to the map
   * @param bound a set representing the bounds
   */
  void add_data_aray(string name, Set bound);

  /*!
   * add an index array to the MapIR. Takes the parameters to construct the index
   * array first. Before adding to the list index_arrays it checks that the name
   * is unique in the MapIR
   * @param name a string that is to the map
   * @param input a set representing the input bounds
   * @param output a set representing the output bounds
   */
  void add_index_array(string name, Set input, Set output);

  /*!
   * add an ERSpec to the MapIR. Takes the parameters to construct the ERSpec
   * first. Before adding to the list er_specs it checks that the name is unique
   * in the MapIR
   * @param name a string that is to the map
   * @param input a set representing the input bounds
   * @param output a set representing the output bounds
   * @param rel a relation representing the relation this ERSpec represents
   */
  void add_er_spec(string name, Set input, Set output, Relation rel);

  /*!
   * add a statement to the MapIR. Takes the parameters to construct the
   * statement first. Before adding to the list statementss it checks that the
   * name is unique in the MapIR
   * @param name a string that is to the map
   * @param text a string of the statements text in the program
   * @param space a set of the iteration space of the statement
   * @param sc a relation for the scattering function for the statement
   */
  void add_statement(string name, string text, Set space, Relation sc);

  /*!
   * add an access_relation to the MapIR. Takes the parameters to construct an
   * access_relation first. Before adding to the list in the given statement it
   * checks to make sure the name is unique in the MapIR
   * @param statemnet_name a string that name of the statement
   * @param name a string that is to the map
   * @param text a string of the statements text in the program
   * @param space a set of the iteration space of the statement
   * @param sc a relation for the scattering function for the statement
   */
  void add_access_relation(string statement_name, string name,
                           string data_array_name, Relation it_to_data);

  /*!
   * add a data_dep to the MapIR. Takes the parameters to construct a data_dep
   * Before adding to the list in the mapIR it checks to make sure the name is
   * unique in the MapIR
   * @param name a string that is to the map
   * @param dep a relation
   */
  void add_data_dep(string name, Relation dep);

  /*!
   * get symbolics returns the list of symbolics in the mapIR
   * @return list of symbolics
   */
  const list<Symbolic> get_symbolics() const;

  /*!
   * get data arrays returns the list of DataArrays in the mapIR
   * @return list of DataArrays
   */
  const list<DataArray> get_data_arrays() const;

  /*!
   * get IndexArrays returns the list of IndexArrays in the mapIR
   * @return list of IndexArrays
   */
  const list<IndexArray> get_index_arrays() const;

  /*!
   * get erspecs returns the list of erspecs in the mapIR
   * @return list of ERspecs
   */
  const list<ERSpec> get_er_specs() const;

  /*!
   * get statements returns the list of statements in the mapIR
   * @return list of statements
   */
  const list<Statement> get_statements() const;

  /*!
   * get symbolics returns the list of symbolics in the mapIR
   * @return list of symbolics
   */
  const list<DataDependence> get_data_deps() const;

  /*!
   * get symbolic returns a specific symbolic from the mapIR
   * @param name the string of name for the symbolic requested
   * @return the symbolic requested
   */
  const Symbolic get_symbolic(string name) const;

  /*!
   * get data_array returns a specific data_array from the mapIR
   * @param name the string of name for the data_array requested
   * @return the data_array requested
   */
  const DataArray get_data_array(string name) const;

  /*!
   * get index_array returns a specific index_array from the mapIR
   * @param name the string of name for the index_array requested
   * @return the index_array requested
   */
  const IndexArray get_index_array(string name) const;

  /*!
   * get er_spec returns a specific er_spec from the mapIR
   * @param name the string of name for the er_spec requested
   * @return the er_spec requested
   */
  const ERSpec get_er_spec(string name) const;

  /*!
   * get statement returns a specific statement from the mapIR
   * @param name the string of name for the statement requested
   * @return the statement requested
   */
  const Statement get_statement(string name) const;

  /*!
   * get access_relation returns a specific access_relation from the mapIR
   * @param statement_name the string of name for the statement that holds the
   * access relation
   * @param name the string of name for the access_relation requested
   * @return the access_relation requested
   */
  const AccessRelation get_access_relation(string statement_name,
                                           string name) const;

  /*!
   * get data_dep returns a specific data_dep from the mapIR
   * @param name the string of name for the data_dep requested
   * @return the data_dep requested
   */
  const DataDependence get_data_dep(string name) const;

private:

  ///set of string representation of the names
  set<string> names;
  ///map of string names to symbolics
  map<string, Symbolic> symbolics;
  ///map of string names to DataArrays
  map<string, DataArray> data_arrays;
  ///map of string names to index arrays
  map<string, IndexArray> index_arrays;
  ///map of string names to er specs
  map<string, ERSpec> er_specs;
  ///map of string names to statements
  map<string, Statement> statements;
  ///map of string names to data dependences
  map<string, DataDependence> data_deps;
};

/*!
 * \class MapIREntity
 * \brief Class holds the names of entities in a Mapping Intermediate
 *        Representation
 */
class MapIREntity{
public:

  /*
   * MapIREntity Constructor
   *
   * @param name string to a name
   */
  inline MapIREntity(string name):name(name){};
  inline const string get_name() const {return name;}
protected:
  string name;
};

/*!
 * \class DataArray
 * \brief MapIrEntity Class representing DataArrays
 */
class DataArray: public MapIREntity{
public:

  /*!
   * DataArray Constructor
   *
   * @param name a string that is to the map
   * @param bound a set representing the bounds
   */
  inline DataArray(string name, Set bound):MapIREntity(name), bounds(bound){};
  const Set get_bound() const;
private:
  ///a set representing the bounds of the dataArray
  Set bounds;
};

/*!
 * \class ERSpec
 * \brief MapIrEntity Class representing ERSpecs
 */
class ERSpec: public MapIREntity{
public:

  /*!
   * ERSpec Constructor
   *
   * @param name a string that is to the map
   * @param input a set representing the input bounds
   * @param output a set representing the output bounds
   * @param rel a relation representing the relation this ERSpec represents
   */
  inline ERSpec(string name, Set input, Set output, Relation rel):
                  MapIREntity(name), input_bound(input),
                  output_bound(output), relation(rel){};
  inline const Set get_input_bound() const {return input_bound;}
  inline const Set get_output_bound () const {return output_bound;}
  inline const Relation get_relation() const {return relation;}
private:
  ///a set representing the input bound of the ER this ERSepc represents
  Set input_bound;
  ///a set representing the output bound of the ER this ERSepc represents
  Set output_bound;
  ///a relation representing the relation of the ER this ERSepc represents
  Relation relation;
};

/*!
 * \class IndexArray
 * \brief MapIrEntity Class representing IndexArray
 */
class IndexArray: public ERSpec{
public:
  /*!
   * IndexArray Constructor
   *
   * @param name a string that is to the map
   * @param input a set representing the input bounds
   * @param output a set representing the output bounds
   */
  inline IndexArray(string name, Set input, Set output):
                      ERSpec(name, input,output, Relation("{[]->[]}")){};
};

/*!
 * \class Statement
 * \brief MapIrEntity Class representing Statement
 */
class Statement: public MapIREntity{
public:

  /*!
   * Statement Constructor
   *
   * @param name a string that is to the map
   * @param text a string of the statements text in the program
   * @param space a set of the iteration space of the statement
   * @param sc a relation for the scattering function for the statement
   */
  inline Statement(string name, string text, Set space, Relation sc):
                      MapIREntity(name), text(text),
                      itr_space(space), scatter(sc){};
  inline const string get_text() const {return text;}
  inline const Set get_itr_space() const {return itr_space;}
  inline const Relation get_scatter() const {return scatter;}
  inline const map<string, AccessRelation> get_access_relations() const
                                           {return access_relations;}

private:
  ///the statenebts test ub the program
  string text;
  ///the iteration space of the statement
  Set itr_space;
  ///relation for the scattering function for the statement
  Relation scatter;
  ///mapping of access relation name to AccessRelation object
  map<string, AccessRelation> access_relations;

  friend const AccessRelation MapIR::get_access_relation(string statement_name,
                                                         string name) const;
  const AccessRelation get_access_relation(string name) const;

  friend void MapIR::add_access_relation(string statement_name, string name,
                                         string data_array_name,
                                         Relation it_to_data);
  void add_access_relation(string name, string data_array_name,
                           Relation it_to_data);

};

/*!
 * \class AccessRelation
 * \brief MapIrEntity Class representing AccessRelation
 */
class AccessRelation: public MapIREntity{
public:

  /*!
   * AccessRelation Constructor
   *
   * @param statemnet_name a string that name of the statement
   * @param name a string that is to the map
   * @param text a string of the statements text in the program
   * @param space a set of the iteration space of the statement
   * @param sc a relation for the scattering function for the statement
   */
  inline AccessRelation(string name, string data_array_name,
                        Relation it_to_data):
                           MapIREntity(name), data_array(data_array_name),
                           iter_to_data(it_to_data){};
  inline const string get_data_array() const {return data_array;}
  inline const Relation get_iter_to_data() const {return iter_to_data;}
private:
  ///name of the data array being accessed
  string data_array;
  ///Relation mapping iterators to positions in the data array that are accessed
  Relation iter_to_data;
};

/*!
 * \class DataDependence
 * \brief MapIrEntity Class representing DataDependence
 */
class DataDependence: public MapIREntity{
public:

  /*!
   * DataDependence Constructor
   *
   * @param name a string that is to the map
   * @param dep a relation
   */
  inline DataDependence(string name, Relation dep):
                           MapIREntity(name), dep_rel(dep){};
  inline const Relation get_dep_rel() const {return dep_rel;}
private:
  ///a Relation that represents the data dependence relation
  Relation dep_rel;
};

}//end namespace iegen

#endif
